home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / Sessions / Traut / ZStrings / Source / CrossPlatform / ZString.cpp next >
Encoding:
C/C++ Source or Header  |  2001-06-23  |  18.1 KB  |  669 lines

  1. /*==================================================================
  2.     File:        ZString.cpp
  3.     
  4.     Contains:    Class that implements a generic container
  5.                 container for storing and manipulating
  6.                 string (textual) data. 
  7.  
  8.     Written by:    Eric Traut
  9.     
  10.     Copyright:    2000-2001 Connectix Corporation
  11.     
  12.     This source has been placed into the public domain by
  13.     Connectix Corporation. You have the right to modify, 
  14.     distribute or use this code without any legal limitations
  15.     or finanicial/licensing requirements. Connectix is not 
  16.     liable for any problems that result from the use of this 
  17.     code.
  18.     
  19.     If you have comments, feedback, questions, or would like
  20.     to submit bug fixes or updates to this code, please email
  21.     opensource@connectix.com.
  22. ==================================================================*/
  23.  
  24. #include "ZString.h"
  25. #include "ZStringParser.h"
  26. #include "ZStringDictionary.h"
  27.  
  28. #include <stdio.h>
  29.  
  30. /*==================================================================
  31.  
  32.     EXPLANATION:
  33.     
  34.     A "ZString" is a class that encapsulates an immutable string
  35.     in a cross-platform and language-neutral manner. They are used 
  36.     to solve problems involving cross-platform and multi-language
  37.     text strings.
  38.     
  39.     The ZString class is also useful for encapsulating and
  40.     manipulating strings in a cross-platform manner. It stores
  41.     all strings internally as 8-bit characters. The code could
  42.     be extended to support unicode in the future.
  43.     
  44.     A ZString is a very small object itself. It simply contains
  45.     a pointer to a reference-counted structure that contains the
  46.     actual string data. Assignment, therefore, is very simple.
  47.     Other operations require the creation of new data blocks.
  48.     All data blocks get cleaned up automatically when their 
  49.     reference counts reach zero again.
  50.     
  51.     A ZString can be constructed in two ways:
  52.         
  53.         ZString zString;                        // Creates an empty string
  54.     or
  55.         ZString zString("This is the string");    // Allocates space for copy of string
  56.         
  57.     Once created, the string can be assigned to or operated
  58.     on in several ways. For example:
  59.     
  60.     zStringA += zStringB;                    // Concatenation
  61.     zStringA = "Hello";                        // Assignment
  62.     zStringA = zStringB + " Dollars";        // Concatenation/Assignment
  63.     zStringA.ReleaseData();                    // Sets to empty string
  64.     zStringA = "";                            // Also sets to empty string
  65.     
  66.     Another method for setting the string data provides the full
  67.     power of ZStrings. Using this method, the string gets a
  68.     full name, as in the following example:
  69.     
  70.     zStringA.GetNamedString("<Z name=VPC/PrefDialog/WindowTitle>Preferences<\Z>");
  71.     
  72.     Note the HTLM/XML syntax. Because the string is given a name, 
  73.     it can be tracked and even replaced in an internal dictionary. 
  74.     This allows for easy localization of the strings, as an override 
  75.     library can be constructed that predefines all the program's 
  76.     strings in a different langauge.
  77.     
  78.     "Named ZStrings" require a simple runtime dictionary that
  79.     tracks these strings as they are encountered in the program.
  80.     Once one of these strings is encountered, a new ZString data
  81.     block is created, and it is entered into the dictionary. Because
  82.     the dictionary "references" that data block, its reference
  83.     count will never go to zero until the entire dictionary is
  84.     destroyed (at program termination time).
  85.  
  86.     The other powerful property of named ZStrings is that simple
  87.     textual replacement can be done. Take the following example:
  88.         
  89.         zString.GetNamedString("<Z name=VPC/ResetDialog/MainText>"
  90.             "Are you sure you want to reset &ldquo&replace00&rdquo&hellip"
  91.             "<\Z>");
  92.     
  93.     This string contains a number of interesting pieces that get
  94.     replaced before the string is entered into the dictionary.
  95.     First, the &ldquo and &rdquo get turned into their
  96.     appropriate "double curly quote" characters. Second, the &hellip
  97.     is replaced by a true ellipsis. The &relace00 is left as-is within
  98.     the string. It is a place-holder for programmatic replacement
  99.     at a later time.
  100.  
  101.     The following metacharacters are defined. Their names are taken
  102.     directly from the HTML 4.0 standard which uses ISO standard names
  103.     for its character sets.
  104.     
  105.         Typography
  106.         ----------
  107.                               Non-breaking space
  108.             &bull                Bullet
  109.             &ndash                En dash
  110.             &mdash                Em dash
  111.             <br>                Line break
  112.  
  113.         Punctuation
  114.         -----------
  115.             ¡                Inverted exclamation point (Spanish)
  116.             ¿                Inverted question mark
  117.             &hellip                Horizontal ellipsis
  118.             &lsquo                Left single quotation mark
  119.             &rsquo                Right single quotation mark
  120.             &sbquo                Single low-9 quotation mark
  121.             &ldquo                Left double quotation mark
  122.             &rdquo                Right double quotation mark
  123.             &bdquo                Double low-9 quotation mark
  124.         
  125.         Symbols
  126.         -------
  127.             ¢                Cent symbol (American)
  128.             £                Pound sign (British)
  129.             ¥                Yen symbol (Japanese)
  130.             ©                Copyright symbol
  131.             ®                Registered trademark
  132.             &trade                Trade mark sign
  133.             µ                Micro sign (Greek mu)
  134.             ¶                Paragraph symbol
  135.             &pi                    Greek small letter pi
  136.             &                Ampersand
  137.             <                    Less than
  138.             >                    Greater than
  139.  
  140.         Foreign Characters
  141.         ------------------
  142.             À                Capital A, grave accent
  143.             Á                Capital A, acute accent
  144.             Â                Capital A, circumflex accent
  145.             Ã                Capital A, tilde
  146.             Ä                Capital A, umlaut
  147.             Å                Capital A, ring
  148.             Æ                Capital AE ligature
  149.             Ç                Capital C, cedilla
  150.             È                Capital E, grave accent
  151.             É                Capital E, acute accent
  152.             Ê                Capital E, circumflex accent
  153.             Ë                Capital E, umlaut
  154.             Ì                Capital I, grave accent
  155.             Í                Capital I, acute accent
  156.             Î                Capital I, circumflex accent
  157.             Ï                Capital I, umlaut
  158.             Ñ                Capital N, tilde
  159.             Ò                Capital O, grave accent
  160.             Ó                Capital O, acute accent
  161.             Ô                Capital O, circumflex accent
  162.             Õ                Capital O, tilde
  163.             Ö                Capital O, umlaut
  164.             Ø                Capital O, slash
  165.             Ù                Capital U, grave accent
  166.             Ú                Capital U, acute accent
  167.             Û                Capital U, circumflex accent
  168.             Ü                Capital U, umlaut
  169.             ß                Small sz ligature, German
  170.             à                Lowercase a, grave accent
  171.             á                Lowercase a, acute accent
  172.             â                Lowercase a, circumflex accent
  173.             ã                Lowercase a, tilde
  174.             ä                Lowercase a, umlaut
  175.             å                Lowercase a, ring
  176.             æ                Lowercase ae ligature
  177.             ç                Lowercase c, cedilla
  178.             è                Lowercase e, grave accent
  179.             é                Lowercase e, acute accent
  180.             ê                Lowercase e, circumflex accent
  181.             ë                Lowercase e, umlaut
  182.             ì                Lowercase i, grave accent
  183.             í                Lowercase i, acute accent
  184.             î                Lowercase i, circumflex accent
  185.             ï                Lowercase i, umlaut
  186.             ñ                Lowercase n, tilde
  187.             ò                Lowercase o, grave accent
  188.             ó                Lowercase o, acute accent
  189.             ô                Lowercase o, circumflex accent
  190.             õ                Lowercase o, tilde
  191.             ö                Lowercase o, umlaut
  192.             ø                Lowercase o, slash
  193.             ù                Lowercase u, grave accent
  194.             ú                Lowercase u, acute accent
  195.             û                Lowercase u, circumflex accent
  196.             ü                Lowercase u, umlaut
  197.             ÿ                Lowercase y, umlaut
  198.  
  199.     The following metacharacters have been added for our own use:
  200.         
  201.         Replacement
  202.         -----------
  203.             &replaceNN            Text replacement (NN is two-digit decimal number)
  204.  
  205.  
  206.     Replacing parameters within a string is easy. Just use the
  207.     following syntax:
  208.     
  209.         zStringA = zStringB.ReplaceParameter(0, "1,000");
  210.     or
  211.         zStringA = zStringB.ReplaceParameter(2, length == 1 ? "meter" : "meters");
  212.  
  213.     There are several rules you need to follow when creating a
  214.     named ZString.
  215.     
  216.     1. You must enclose them in a <Z> <\Z> pair of tags.
  217.     2. You must provide a "name" parameter in the <Z> tag.
  218.     3. The actual string must consist of only 7-bit ASCII
  219.         characters. For more complex characters, use the
  220.         above metacharacters. The only 7-bit characters you 
  221.         can't encode directly are the ampersand, less than
  222.         or greater than. To encode these characters, use 
  223.         the "&", "<", and ">" metacharacters.
  224.     4. You can't create ZStrings at static initialization
  225.         time. The registration dictionary is not yet created
  226.         and the override library hasn't yet been loaded at
  227.         this time.
  228.     
  229. ==================================================================*/
  230.  
  231.  
  232.  
  233. /*------------------------------------------------------------------
  234.     ZString
  235. ------------------------------------------------------------------*/
  236.  
  237. ZString::ZString(
  238.     const char *        inString)
  239.     :    mData(NULL)
  240. {
  241.     Z_UInt32 stringLength = strlen(inString);
  242.  
  243.     if (stringLength != 0)
  244.     {
  245.         AllocateData(stringLength);
  246.         FillInString(inString, 0, stringLength);
  247.     }
  248. }
  249.  
  250.  
  251. /*------------------------------------------------------------------
  252.     GetNamedString
  253.     
  254.     This method looks up the specified named string in the
  255.     dictionary, creating a new dictionary entry if it's not
  256.     already present. The actual string data is stored in the
  257.     object.
  258.     
  259.     The inDataIsVolatile should be true if the name string 
  260.     may go away or move throughout the execution of the program.
  261.     In most environments, strings are stored as const arrays in
  262.     the binary, so we can just store a pointer to them within the
  263.     dictionary. However, if they are going to move, we need
  264.     to allocate additional space and make a copy.
  265. ------------------------------------------------------------------*/
  266.  
  267. void
  268. ZString::GetNamedString(
  269.     const char *        inNamedString,
  270.     Z_Boolean            inDataIsVolatile)
  271. {
  272.     ZStringParser &         parser = ZStringParser::GetZStringParser();
  273.     ZStringDictionary &     dictionary = ZStringDictionary::GetZStringDictionary();
  274.     ZStringParseInfo         parseInfo;
  275.  
  276.     if (parser.ParseNamedString(inNamedString, parseInfo, inDataIsVolatile))
  277.     {
  278.         if (!dictionary.LookUpString(parseInfo, *this))
  279.             parser.CreateNewZString(parseInfo, *this);
  280.     }
  281. }
  282.  
  283.  
  284. /*------------------------------------------------------------------
  285.     GetNamedZString                                    [static]
  286.     
  287.     This method is a static form of GetNameString and returns a 
  288.     newly created ZString object.
  289. ------------------------------------------------------------------*/
  290.  
  291. ZString
  292. ZString::GetNamedZString(
  293.     const char *        inNamedString,
  294.     Z_Boolean            inDataIsVolatile)
  295. {
  296.     ZString        newString;
  297.     
  298.     newString.GetNamedString(inNamedString, inDataIsVolatile);
  299.     
  300.     return newString;
  301. }
  302.  
  303.         
  304. /*------------------------------------------------------------------
  305.     PopulateDictionary                                [static]
  306. ------------------------------------------------------------------*/
  307.  
  308. void
  309. ZString::PopulateDictionary(
  310.     const char *        inOverrideDict,
  311.     Z_Boolean            inDataIsVolatile)
  312. {
  313.     ZStringParser &         parser = ZStringParser::GetZStringParser();
  314.     ZStringDictionary &     dictionary = ZStringDictionary::GetZStringDictionary();
  315.     ZStringParseInfo         parseInfo;
  316.     const char *            curDictPtr = inOverrideDict;
  317.  
  318.     while (*curDictPtr != '\0')
  319.     {
  320.         if (parser.ParseNamedString(curDictPtr, parseInfo, inDataIsVolatile))
  321.         {
  322.             ZString        tempZString;
  323.             if (!dictionary.LookUpString(parseInfo, tempZString))
  324.                 parser.CreateNewZString(parseInfo, tempZString);
  325.         }
  326.         
  327.         // Move on to the next override value.
  328.         curDictPtr += parseInfo.fNamedStringLimit - parseInfo.fNamedStringStart + 1;
  329.     }
  330. }
  331.  
  332.  
  333. /*------------------------------------------------------------------
  334.     AllocateData
  335. ------------------------------------------------------------------*/
  336.  
  337. void
  338. ZString::AllocateData(
  339.     Z_UInt32            inStringLength)
  340. {
  341.     // Release the old data.
  342.     ReleaseData();
  343.     
  344.     mData = ZStringData::Allocate(inStringLength);
  345.     if (mData != NULL)
  346.         mData->IncrementRefCount();
  347. }
  348.  
  349.  
  350. /*------------------------------------------------------------------
  351.     GetCString
  352. ------------------------------------------------------------------*/
  353.  
  354. Z_UInt16
  355. ZString::GetCString(
  356.     char *                outString,
  357.     Z_UInt16            inBufferSize)
  358. {
  359.     Z_UInt32        cStrLen;
  360.     
  361.     check(inBufferSize > 0);
  362.     
  363.     if (mData == NULL)
  364.     {
  365.         cStrLen = 0;
  366.     }
  367.     else
  368.     {
  369.         cStrLen = mData->GetLength();
  370.         if (cStrLen > inBufferSize - 1)
  371.             cStrLen = inBufferSize - 1;
  372.         
  373.         memcpy(outString, mData->GetStringData(), cStrLen);
  374.         outString[cStrLen] = 0;
  375.     }
  376.     
  377.     check(cStrLen == (Z_UInt16)cStrLen);
  378.  
  379.     return (Z_UInt16)cStrLen;
  380. }
  381.  
  382.  
  383. /*------------------------------------------------------------------
  384.     GetPString
  385. ------------------------------------------------------------------*/
  386.  
  387. Z_UInt8
  388. ZString::GetPString(
  389.     Z_UInt8 *            outString,
  390.     Z_UInt16            inBufferSize)
  391. {
  392.     Z_UInt32        pStrLen;
  393.     
  394.     check(inBufferSize > 0);
  395.     
  396.     // Pascal strings can be at most 255 characters.
  397.     // It doesn't make sense for the buffer size to be more.
  398.     check(inBufferSize <= 256);
  399.     if (inBufferSize > 256)
  400.         inBufferSize = 256;
  401.     
  402.     if (mData == NULL)
  403.     {
  404.         pStrLen = 0;
  405.     }
  406.     else
  407.     {
  408.         pStrLen = mData->GetLength();
  409.         if (pStrLen > inBufferSize - 1)
  410.             pStrLen = inBufferSize - 1;
  411.         
  412.         memcpy(&outString[1], mData->GetStringData(), pStrLen);
  413.     }
  414.  
  415.     outString[0] = pStrLen;
  416.     return (Z_UInt8)pStrLen;
  417. }
  418.  
  419.  
  420. /*------------------------------------------------------------------
  421.     GetSubstring
  422.     
  423.     Scans the string for substrings separated by a specified
  424.     escape character. The index is zero-based.
  425. ------------------------------------------------------------------*/
  426.  
  427. ZString
  428. ZString::GetSubstring(
  429.     Z_UInt16            inIndex,
  430.     char                inSeparator)
  431. {
  432.     ZString                subString;
  433.     Z_UInt16            subStringsFound = 0;
  434.     Z_UInt32            totalLength = GetLength();
  435.     Z_UInt32            charsLeft = totalLength;
  436.     const char *        scanPtr;
  437.     
  438.     if (mData != NULL)
  439.     {
  440.         scanPtr = mData->GetStringData();
  441.         
  442.         while (true)
  443.         {
  444.             // Have we found the start of the specified string?
  445.             if (subStringsFound == inIndex)
  446.             {
  447.                 const char *        startOfSubString = scanPtr;
  448.                 
  449.                 // Scan ahead for the end of the string or the
  450.                 // next separator.
  451.                 while (charsLeft > 0 && *scanPtr != inSeparator)
  452.                 {
  453.                     scanPtr++;
  454.                     charsLeft--;
  455.                 }
  456.                 
  457.                 // Set the string.
  458.                 if (scanPtr != startOfSubString)
  459.                     subString.SetString(startOfSubString, scanPtr - startOfSubString);
  460.                 
  461.                 break;
  462.             }
  463.             
  464.             if (*scanPtr == inSeparator)
  465.                 subStringsFound++;
  466.             
  467.             scanPtr++;
  468.             charsLeft--;
  469.             
  470.             if (charsLeft == 0)
  471.                 break;
  472.         }
  473.     }
  474.     
  475.     return subString;
  476. }
  477.  
  478.  
  479. /*------------------------------------------------------------------
  480.     SetString
  481. ------------------------------------------------------------------*/
  482.  
  483. void
  484. ZString::SetString(
  485.     const char *        inString,
  486.     Z_UInt16            inLength)
  487. {
  488.     if (inLength == 0)
  489.     {
  490.         ReleaseData();
  491.     }
  492.     else
  493.     {
  494.         AllocateData(inLength);
  495.         FillInString(inString, 0, inLength);
  496.     }
  497. }
  498.  
  499.  
  500. /*------------------------------------------------------------------
  501.     SetCString
  502. ------------------------------------------------------------------*/
  503.  
  504. void
  505. ZString::SetCString(
  506.     const char *        inString)
  507. {
  508.     SetString(inString, strlen(inString));
  509. }
  510.  
  511.  
  512. /*------------------------------------------------------------------
  513.     SetPString
  514. ------------------------------------------------------------------*/
  515.  
  516. void
  517. ZString::SetPString(
  518.     const Z_UInt8 *        inString)
  519. {
  520.     SetString((const char *)inString + 1, inString[0]);
  521. }
  522.  
  523.  
  524. /*------------------------------------------------------------------
  525.     operator =
  526. ------------------------------------------------------------------*/
  527.  
  528. ZString
  529. ZString::operator = (
  530.     ZString &             inString)
  531. {
  532.     ReleaseData();
  533.     mData = inString.mData;
  534.     if (mData != NULL)
  535.         mData->IncrementRefCount();
  536.     
  537.     return *this;
  538. }
  539.  
  540.  
  541. /*------------------------------------------------------------------
  542.     ConcatStrings
  543. ------------------------------------------------------------------*/
  544.  
  545. void
  546. ZString::ConcatStrings(
  547.     const char *        inStringA,
  548.     Z_UInt16            inStringALen,
  549.     const char *        inStringB,
  550.     Z_UInt16            inStringBLen)
  551. {
  552.     AllocateData(inStringALen + inStringBLen);
  553.     FillInString(inStringA, 0, inStringALen);
  554.     FillInString(inStringB, inStringALen, inStringBLen);
  555. }
  556.  
  557.  
  558. /*------------------------------------------------------------------
  559.     ConcatString
  560. ------------------------------------------------------------------*/
  561.  
  562. void
  563. ZString::ConcatString(
  564.     const char *        inString,
  565.     Z_UInt16            inStringLen)
  566. {
  567.     ZStringData *        oldData = mData;
  568.     Z_UInt16            oldLength = GetLength();
  569.     
  570.     // This is a little gross. We're going to "unhook" the
  571.     // data from this object temporarily. This allows us
  572.     // to allocate a new block without deallocating the
  573.     // existing one.
  574.     mData = NULL;
  575.     
  576.     AllocateData(oldLength + inStringLen);
  577.     if (oldData != NULL)
  578.         FillInString(oldData->GetStringData(), 0, oldLength);
  579.     FillInString(inString, oldLength, inStringLen);
  580.     
  581.     // Now, decrement the ref count on the old data.
  582.     // If no one else is accessing it, it will dispose
  583.     // of itself.
  584.     if (oldData != NULL)
  585.         oldData->DecrementRefCount();
  586. }
  587.  
  588.  
  589. /*------------------------------------------------------------------
  590.     ReplaceParameter
  591. ------------------------------------------------------------------*/
  592.  
  593. ZString
  594. ZString::ReplaceParameter(
  595.     Z_UInt8                inParamNum,
  596.     const char *        inString)
  597. {
  598.     // We only support decimal numbers from 00 to 99.
  599.     check(inParamNum < 100);
  600.     
  601.     if (mData != NULL)
  602.     {
  603.         char            replaceString[32];
  604.         const char *    replMetaChar;
  605.         
  606.         sprintf(replaceString, "&replace%02d", inParamNum);
  607.         
  608.         // Look for the replacement string.
  609.         replMetaChar = strstr(mData->GetStringData(), replaceString);
  610.         check(replMetaChar != NULL);
  611.         
  612.         // If we didn't find it, don't replace anything.
  613.         if (replMetaChar != NULL)
  614.         {
  615.             // Calculate the new string size.
  616.             Z_UInt16 replaceTextLen = strlen(inString);
  617.             Z_UInt16 newStringLen = mData->GetLength() - 10 + replaceTextLen;
  618.             
  619.             ZStringData * oldData = mData;
  620.             
  621.             // We're going to "unhook" the data from this object 
  622.             // temporarily. This allows us to allocate a new block 
  623.             // without deallocating the existing one.
  624.             mData = NULL;
  625.             
  626.             AllocateData(newStringLen);
  627.             
  628.             Z_UInt16 firstRunLen = replMetaChar - oldData->GetStringData();
  629.             
  630.             // Copy the portion before the metacharacter.
  631.             if (firstRunLen != 0)
  632.                 FillInString(oldData->GetStringData(), 0, firstRunLen);
  633.             
  634.             // Copy the text we're inserting.
  635.             if (replaceTextLen != 0)
  636.                 FillInString(inString, firstRunLen, replaceTextLen);
  637.             
  638.             // Copy the remainder.
  639.             if (firstRunLen + replaceTextLen < newStringLen)
  640.                 FillInString(replMetaChar + 10, firstRunLen + replaceTextLen, newStringLen - firstRunLen - replaceTextLen);
  641.             
  642.             // Now, decrement the ref count on the old data.
  643.             // If no one else is accessing it, it will dispose
  644.             // of itself.
  645.             oldData->DecrementRefCount();
  646.         }
  647.     }
  648.     
  649.     return *this;
  650. }
  651.  
  652.  
  653. /*------------------------------------------------------------------
  654.     ReplaceParameter
  655. ------------------------------------------------------------------*/
  656.  
  657. ZString
  658. ZString::ReplaceParameter(
  659.     Z_UInt8                inParamNum,
  660.     Z_SInt32            inNumber)
  661. {
  662.     char stringBuf[16];
  663.     sprintf(stringBuf, "%d", inNumber);
  664.     
  665.     return ReplaceParameter(inParamNum, stringBuf);
  666. }
  667.  
  668.  
  669.